<template>
  <el-table
    :data="tableData"
    v-loading="isLoading"
    :element-loading-text="elementLoadingText"
    :element-loading-background="elementLoadingBackground"
    :element-loading-spinner="elementLoadingSpinner"
    :element-loading-svg="elementLoadingSvg"
    :element-loading-svg-view-box="elementLoadingSvgViewBox"
    @row-click="handleRowClick"
  >
    <template v-for="(item, index) in tableOptions" :key="index">
      <el-table-column
        :label="item.label"
        :prop="item.prop"
        :align="item.align"
        :width="item.width"
      >
        <template #default="scope">
          <template v-if="scope.row.isEditRow">
            <el-input v-model="scope.row[item.prop]" />
          </template>

          <template v-else>
            <template v-if="(scope.$index + '_' + scope.column.id) === currentEdit">
              <div class="edit-cell-container">
                <el-input v-model="scope.row[item.prop]" />
                <div class="edit-actions" @click.stop="clickEditCell">
                  <slot v-if="$slots.editCell" name="editCell" :scope="scope" />
                  <template v-else>
                    <el-icon class="action-icon confirm" @click="confirmEdit(scope)">
                      <el-icon-check />
                    </el-icon>
                    <el-icon class="action-icon cancel" @click="cancelEdit(scope)">
                      <el-icon-close />
                    </el-icon>
                  </template>
                </div>
              </div>
            </template>
            <template v-else>
              <slot v-if="item.slot" :name="item.slot" :scope="scope" />
              <span v-else>{{ scope.row[item.prop] }}</span>
              <component
                v-if="item.editable"
                class="edit-icon"
                :is="editIcon"
                @click.stop="clickEdit(scope)"
              />
            </template>
          </template>
        </template>
      </el-table-column>
    </template>

    <el-table-column
      v-if="actionOptions"
      :label="actionOptions.label"
      :align="actionOptions.align"
      :width="actionOptions.width"
    >
      <template #default="scope">
        <slot v-if="scope.row.isEditRow" name="editRow" :scope="scope" />
        <slot v-else name="action" :scope="scope" />
      </template>
    </el-table-column>
  </el-table>
</template>

<script setup lang="ts">
import { ref, computed, watch, onMounted } from 'vue'
import { cloneDeep } from 'lodash-es'
import type { PropType } from 'vue'
import type { TableColumnCtx } from 'element-plus'
import type { TableOptions, TableScope, TableEmits } from './types'

defineOptions({
  name: 'VTable'
})

const props = defineProps({
  options: {
    type: Array as PropType<TableOptions[]>,
    required: true,
    validator: (val: TableOptions[]) => val.some(item => item.action)
  },
  data: {
    type: Array as PropType<any[]>,
    required: true
  },
  elementLoadingText: String,
  elementLoadingSpinner: String,
  elementLoadingBackground: String,
  elementLoadingSvg: String,
  elementLoadingSvgViewBox: String,
  editIcon: {
    type: String,
    default: 'el-icon-edit'
  },
  isEditRow: {
    type: Boolean,
    default: false
  },
  editRowIndex: {
    type: String,
    default: ''
  },
  pagination: Boolean,
  paginationAlign: {
    type: String as PropType<'left' | 'center' | 'right'>,
    default: 'center',
    validator: (val: string) => ['left', 'center', 'right'].includes(val)
  }
})

const emit = defineEmits<TableEmits>()

// Refs
const currentEdit = ref<string>('')
const tableData = ref<any[]>(cloneDeep(props.data))
const cloneEditRowIndex = ref<string>(props.editRowIndex)

// Computed
const tableOptions = computed(() => props.options.filter(item => !item.action))
const actionOptions = computed(() => props.options.find(item => item.action))
const isLoading = computed(() => !props.data || !props.data.length)

// Watchers
watch(() => props.data, (val) => {
  tableData.value = cloneDeep(val)
  initRowEditState()
}, { deep: true })

watch(() => props.editRowIndex, (val) => {
  cloneEditRowIndex.value = val || ''
})

// Lifecycle
onMounted(initRowEditState)

// Methods
function initRowEditState() {
  tableData.value.forEach(item => {
    item.rowEdit = false
    item.isEditRow = false
  })
}

function clickEdit(scope: TableScope) {
  currentEdit.value = `${scope.$index}_${scope.column.id}`
  emit('edit-click', scope)
}

function clickEditCell() {
  currentEdit.value = ''
}

function confirmEdit(scope: TableScope) {
  emit('confirm', scope)
  currentEdit.value = ''
}

function cancelEdit(scope: TableScope) {
  emit('cancel', scope)
  currentEdit.value = ''
}

function handleRowClick(row: any, column: TableColumnCtx<any>) {
  if (column.label === actionOptions.value?.label) {
    if (props.isEditRow && cloneEditRowIndex.value === props.editRowIndex) {
      row.rowEdit = !row.rowEdit
      tableData.value.forEach(item => {
        if (item !== row) item.rowEdit = false
      })

      if (!row.rowEdit) {
        emit('update:editRowIndex', undefined)
      }
    }
  }
}
</script>


<style lang="scss" scoped>
.edit-cell-container {
  display: flex;
  align-items: center;
  gap: 8px;
}

.edit-actions {
  display: flex;
  gap: 8px;
}

.action-icon {
  cursor: pointer;
  font-size: 16px;
  
  &.confirm {
    color: var(--el-color-success);
  }
  
  &.cancel {
    color: var(--el-color-danger);
  }
}

.edit-icon {
  width: 1em;
  height: 1em;
  margin-left: 4px;
  cursor: pointer;
  color: var(--el-color-primary);
}
</style>